package com.inspectortime.domain.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.hibernate.annotations.CollectionOfElements;

import com.inspectortime.common.PersistentEntityBase;
import com.inspectortime.domain.ScheduleRule;
import com.inspectortime.domain.type.FrequencyType;
import com.inspectortime.domain.type.Time;
import com.inspectortime.timestamp.Timestamped;
import com.inspectortime.util.CalendarUtil;

@Entity
@Table(name = "SCHEDULE_RULE")
public class ScheduleRuleImpl extends PersistentEntityBase implements
		ScheduleRule, Timestamped {

	private Date createdTime;
	private Date lastModifiedTime;

	public Date getCreatedTime() {
		return createdTime;
	}

	public void setCreatedTime(Date createdTime) {
		this.createdTime = createdTime;
	}

	public Date getLastModifiedTime() {
		return lastModifiedTime;
	}

	public void setLastModifiedTime(Date lastModifiedTime) {
		this.lastModifiedTime = lastModifiedTime;
	}

	private FrequencyType frequencyType;
	private int startHour;
	private int startMinute;
	private int endHour;
	private int endMinute;
	private Calendar startDate;
	private Calendar endDate;
	boolean available;

	@CollectionOfElements
	@JoinTable(name = "SCHEDULE_RULE_UNIT", joinColumns = { @JoinColumn(name = "schedule_rule_id") })
	@Column(name = "unit", nullable = false)
	private int[] units;

	public ScheduleRuleImpl() {
	}

	public ScheduleRuleImpl(FrequencyType frequencyType, int startHour,
			int startMinute, int endHour, int endMinute) {
		this.frequencyType = frequencyType;
		this.startHour = startHour;
		this.startMinute = startMinute;
		this.endHour = endHour;
		this.endMinute = endMinute;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.inspectortime.domain.ScheduleRule#setFrequencyType(com.inspectortime
	 * .domain.type.FrequencyType)
	 */
	public void setFrequencyType(FrequencyType frequencyType) {
		this.frequencyType = frequencyType;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getFrequencyType()
	 */
	public FrequencyType getFrequencyType() {
		return frequencyType;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getUnits()
	 */
	public int[] getUnits() {
		return units;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#setUnits(int[])
	 */
	public void setUnits(int[] units) {
		this.units = units;

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getUnitsForDisplay()
	 */
	@Transient
	public List<String> getUnitsForDisplay() {
		List<String> unitsForDisplay = new ArrayList<String>();
		if (this.units != null) {
			for (int unit : this.units) {
				unitsForDisplay.add(CalendarUtil.getDisplayableDayOfWeek(unit));
			}
		}
		return unitsForDisplay;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.inspectortime.domain.ScheduleRule#setStartDate(java.util.Calendar)
	 */
	public void setStartDate(Calendar startDate) {
		this.startDate = startDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getStartDate()
	 */
	public Calendar getStartDate() {
		return startDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#setEndDate(java.util.Calendar)
	 */
	public void setEndDate(Calendar endDate) {
		this.endDate = endDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getEndDate()
	 */
	public Calendar getEndDate() {
		return endDate;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getStartHour()
	 */
	public int getStartHour() {
		return startHour;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#setStartHour(int)
	 */
	public void setStartHour(int startHour) {
		this.startHour = startHour;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getStartMinute()
	 */
	public int getStartMinute() {
		return startMinute;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#setStartMinute(int)
	 */
	public void setStartMinute(int startMinute) {
		this.startMinute = startMinute;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getEndHour()
	 */
	public int getEndHour() {
		return endHour;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#setEndHour(int)
	 */
	public void setEndHour(int endHour) {
		this.endHour = endHour;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#getEndMinute()
	 */
	public int getEndMinute() {
		return endMinute;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#setEndMinute(int)
	 */
	public void setEndMinute(int endMinute) {
		this.endMinute = endMinute;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#isAvailable()
	 */
	public boolean isAvailable() {
		return available;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.inspectortime.domain.ScheduleRule#setAvailable(boolean)
	 */
	public void setAvailable(boolean available) {
		this.available = available;
	}

	/**
	 * Compare this rule to another rule based on priority of the rule's
	 * FrequencyType
	 */
	public int compareTo(Object other) {
		ScheduleRuleImpl otherRule = (ScheduleRuleImpl) other;
		return this.getFrequencyType().compareTo(otherRule.getFrequencyType());
	}

	public boolean hasConflictingTimes(ScheduleRule compareRule) {
		
		// mads 12/16/09 bug fix: This method needs to compare the times, not the dates, because the Date values are not being set correctly anymore
		/*
		return CalendarUtil.isOverlapping(this.getStartDate(), this
				.getEndDate(), compareRule.getStartDate(), compareRule
				.getEndDate());
		*/
		
		// Create new Date objects and populate them with hours/mins
		Calendar originalStart = CalendarUtil.createDayCalendar();
		originalStart.set(Calendar.HOUR_OF_DAY, this.getStartHour());
		originalStart.set(Calendar.MINUTE, this.getStartMinute());
		
		Calendar originalEnd = CalendarUtil.createDayCalendar();
		originalEnd.set(Calendar.HOUR_OF_DAY, this.getEndHour());
		originalEnd.set(Calendar.MINUTE, this.getEndMinute());
		
		Calendar compareStart = CalendarUtil.createDayCalendar();
		compareStart.set(Calendar.HOUR_OF_DAY, compareRule.getStartHour());
		compareStart.set(Calendar.MINUTE, compareRule.getStartMinute());
		
		Calendar compareEnd = CalendarUtil.createDayCalendar();
		compareEnd.set(Calendar.HOUR_OF_DAY, compareRule.getEndHour());
		compareEnd.set(Calendar.MINUTE, compareRule.getEndMinute());
		
		return CalendarUtil.isOverlapping(originalStart, originalEnd, compareStart, compareEnd);
		
	}

	@Transient
	public Time getStartTime() {
		return new Time(this.startHour, this.startMinute);
	}
	
	@Transient
	public Time getEndTime() {
		return new Time(this.endHour, this.endMinute);
	}

	public boolean conflictsWith(ScheduleRule compareRule) {

		if (this.getFrequencyType() == compareRule.getFrequencyType()) {

			if (this.getFrequencyType() == FrequencyType.DAY_OF_WEEK) {
				if (this.hasConflictingUnits(compareRule)) {

					// Note that DAY_OF_WEEK rules are always recurring and thus
					// don't have a date value set. Therefore we create dates
					// based on today to use for the overlap comparison
					if (CalendarUtil.isOverlapping(this.getStartTime(), 
							this.getEndTime(), compareRule
									.getStartTime(), compareRule
									.getEndTime())) {
						return true;
					}
				}
			} else if (this.getFrequencyType() == FrequencyType.SINGLE_DAY) {
				if (CalendarUtil.isOverlapping(this.startDate, this.endDate,
						compareRule.getStartDate(), compareRule.getEndDate())
						&& this.hasConflictingTimes(compareRule)) {
					return true;
				}
			}
		}

		return false;

	}

	public boolean hasConflictingUnits(ScheduleRule compareRule) {
		for (int existingDayOfWeek : this.getUnits()) {
			for (int newDayOfWeek : compareRule.getUnits()) {
				if (existingDayOfWeek == newDayOfWeek) {
					return true;
				}
			} // for
		} // for
		return false;
	}

	public String toString() {
		StringBuffer buf = new StringBuffer();
		if (this.frequencyType == FrequencyType.DAY_OF_WEEK) {
			buf.append(this.frequencyType);
			buf.append(" ");
			buf.append(this.getUnitsForDisplay());

		} else if (this.frequencyType == FrequencyType.SINGLE_DAY) {
			buf.append(this.frequencyType.toString());
		}

		// buf.append(" ");
		// buf.append(this.getStartTime());
		// buf.append("-");
		// buf.append(this.getEndTime());

		if (this.startDate != null || this.endDate != null) {
			buf.append("(");
		}
		if (this.startDate != null) {
			buf.append("Start Date: ");
			buf.append(this.startDate.getTime());
		}
		if (this.endDate != null) {
			buf.append(" End Date: ");
			buf.append(this.endDate.getTime());
		}

		if (this.startDate != null || this.endDate != null) {
			buf.append(")");
		}

		return buf.toString();
	}

}
